package com.kancy.vip.pattern.proxy.dynamicproxy.myjdk;

import com.kancy.vip.pattern.proxy.Love;
import com.kancy.vip.util.JavaUtil;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

/**
 * @author kancy
 * @date 2019/3/16 17:54
 */
public class MyProxy {
	protected MyInvocattion h;

	public static Object newProxyInstance(ClassLoader classLoader, Class[] intefaces,
	                 MyInvocattion h) throws Exception {
		MyProxy myProxy = new MyProxy();
		Class aClass = myProxy.generatorClass(Love.class.getClassLoader(),new Class[]{Love.class});

		MyProxy o = (MyProxy) aClass.newInstance();
		o.setH(h);
		return o;
	}

	/**
	 * 生成字节码，并且加载代理类
	 * @param classLoader
	 * @param intefaces
	 * @return
	 */
	private Class generatorClass(ClassLoader classLoader, Class[] intefaces) {

		String src = generatorJava(intefaces);

		// 编译java字符串
		Class<?> aClass = JavaUtil.compileAndLoadClass(src,false);
		return aClass;
	}


	private String generatorJava(Class[] intefaces) {

		List<Method> proxyMethodList = new ArrayList<>();
		for (int i = 0; i <intefaces.length ; i++) {
			Class clazz = intefaces[i];
			if(!clazz.isInterface()){
				//throw new IllegalArgumentException(clazz + "is not inteface");
			}
			proxyMethodList.addAll(Arrays.asList(clazz.getMethods()));
		}

		// 对所有方法进行重写实现
		StringBuffer sb = new StringBuffer();
		sb.append("package "+MyProxy.class.getPackage().getName()+";\n");
		sb.append("import com.kancy.vip.pattern.proxy.*;\n");
		sb.append("import java.lang.reflect.*;\n");
		sb.append("public class MyProxy$0 extends MyProxy implements ");
		Stream.of(intefaces).forEach(inteface ->{
			sb.append(inteface.getName());
			sb.append(",");
		});
		sb.deleteCharAt(sb.length()-1);
		sb.append("{\n");
		sb.append("private static Method m0;\n");
		sb.append("private static Method m1;\n");
		sb.append("private static Method m2;\n");
		for (int i = 0; i <proxyMethodList.size() ; i++) {
			sb.append("\t");
			sb.append("private static Method m"+(i+3)+";");
			sb.append("\n");
		}

		sb.append("" +
				"static {\n" +
				"\t\ttry {\n" +
				"\t\t\tm0 = Class.forName(\"java.lang.Object\").getMethod(\"hashCode\");\n" +
				"\t\t\tm1 = Class.forName(\"java.lang.Object\").getMethod(\"equals\", Class.forName(\"java.lang.Object\"));\n" +
				"\t\t\tm2 = Class.forName(\"java.lang.Object\").getMethod(\"toString\");\n" );


		for (int i = 0; i <proxyMethodList.size() ; i++) {

			Method method = proxyMethodList.get(i);
			sb.append("\t\t\t");
			sb.append("m"+(i+3) + "=");
			sb.append("Class.forName(");
			sb.append("\""+ method.getDeclaringClass().getName() +"\"");
			sb.append(")");
			int parameterCount = method.getParameterCount();
			if(parameterCount == 0){
				sb.append(".getMethod(\""+ method.getName()+"\");\n");
			}else{
				Class<?>[] parameterTypes = method.getParameterTypes();
				sb.append(".getMethod(\""+ method.getName()+"\",");
				for (int j = 0; j <parameterCount ; j++) {
					sb.append(parameterTypes[i].getName()+".class");
					sb.append(",");
				}
				sb.deleteCharAt(sb.length()-1);
				sb.append(");\n");
			}

		}


		sb.append(
				"\t\t} catch (NoSuchMethodException var2) {\n" +
				"\t\t\tthrow new NoSuchMethodError(var2.getMessage());\n" +
				"\t\t} catch (ClassNotFoundException var3) {\n" +
				"\t\t\tthrow new NoClassDefFoundError(var3.getMessage());\n" +
				"\t\t}\n" +
				"\t}\n" +
				"");

		// 拼接方法

		sb.append("\t\t" +
				"public final boolean equals(Object var1)   {\n" +
				"\t\ttry {\n" +
				"\t\t\treturn (Boolean)super.h.invoke(this, m1, new Object[]{var1});\n" +
				"\t\t} catch (RuntimeException | Error var3) {\n" +
				"\t\t\tthrow var3;\n" +
				"\t\t} catch (Throwable var4) {\n" +
				"\t\t\tthrow new UndeclaredThrowableException(var4);\n" +
				"\t\t}\n" +
				"\t}\n" +
				"\n" +
				"\tpublic final String toString()   {\n" +
				"\t\ttry {\n System.out.println(super.h);" +
				"\t\t\treturn (String)super.h.invoke(this, m2, (Object[])null);\n" +
				"\t\t} catch (RuntimeException | Error var2) {\n" +
				"\t\t\tthrow var2;\n" +
				"\t\t} catch (Throwable var3) {\n" +
				"\t\t\tthrow new UndeclaredThrowableException(var3);\n" +
				"\t\t}\n" +
				"\t}\n" +
				"\n" +
				"\t\n" +
				"\n" +
				"\tpublic final int hashCode()   {\n" +
				"\t\ttry {\n" +
				"\t\t\treturn (Integer)super.h.invoke(this, m0, (Object[])null);\n" +
				"\t\t} catch (RuntimeException | Error var2) {\n" +
				"\t\t\tthrow var2;\n" +
				"\t\t} catch (Throwable var3) {\n" +
				"\t\t\tthrow new UndeclaredThrowableException(var3);\n" +
				"\t\t}\n" +
				"\t}" +
				"\n\n");

		for (int i = 0; i <proxyMethodList.size() ; i++) {

			Method method = proxyMethodList.get(i);
			Class<?> returnType = method.getReturnType();
			Class<?>[] parameterTypes = method.getParameterTypes();
			String name = method.getName();
			sb.append("public "+ returnType.getName());
			sb.append(" " + name + "( ");
			String pp = "";
			for (int j = 0; j <parameterTypes.length ; j++) {
				sb.append(parameterTypes[i].getName()+" arg"+j + ",");
				pp += ("arg"+j + ",");
			}
			sb.deleteCharAt(sb.length()-1);
			sb.append(") { \n");

			sb.append("\t\ttry {\n" +
					"\t\t\treturn ("+returnType.getName()+")super.h.invoke(this, m"+(i+3)+", " +
					(method.getParameterCount() == 0 ? "(Object[])null":("new Object[]{"+pp.substring(0, pp.length()-1))+"}") +
					"" +
					");\n" +
					"\t\t} catch (RuntimeException | Error var2) {\n" +
					"\t\t\tthrow var2;\n" +
					"\t\t} catch (Throwable var3) {\n" +
					"\t\t\tthrow new UndeclaredThrowableException(var3);\n" +
					"\t\t}");

			sb.append("}\n");
		}

		sb.append("\n");
		sb.append("}");

		System.out.println(sb);


		return sb.toString();

	}

	public void setH(MyInvocattion h) {
		this.h = h;
	}

	public MyInvocattion getH() {
		return h;
	}
}
